
#include "ccrtrtti.h"
#include <exception>
#include <cstdlib>

#define __RTTI_USE_POINTER_TYPE_INFO_COMPARE 1

namespace rtti
{/*
	struct __pmd_t
	{
		int mdisp;  //member displacement
		int pdisp;  //vbtable displacement
		int vdisp;  //displacement inside vbtable
	};	
	*/
	void * __base_class_descriptor_t::compute_this_ptr( void * __cpp_obj ) const
	{
		char * __this = 0;
		if( __cpp_obj != 0 )
		{
			__this = static_cast<char *>(__cpp_obj) + this->where.member_disp;

			if ( this->where.vbtable_disp >= 0 )
			{
				__this += this->where.vbtable_disp;
				int * offset_ptr = reinterpret_cast<int *>( 
										(*reinterpret_cast<char**>(__this) 
																+ this->where.internal_disp  ));
				__this += *offset_ptr;
			}
		}
		return __this;
	}

	struct __type_info_ptr_copare
	{
		inline bool	operator()( type_info const & __a, type_info const & __b ) const
		{
			return (__a.raw_name() == __b.raw_name());
		}
	};
	struct __type_info_name_copare
	{
		inline bool	operator()( type_info const & __a, type_info const & __b ) const
		{
			return ( __a == __b ) != 0 ;
		}
	};

	template<typename __compare>
	inline static void * __rt_dynamic_cast_iterator( void * __cpp_obj
													, type_info const & __cast_type
													, __complete_object_locator_t const & __col
													, __compare const & __comparator )
	{
		void * __this_ptr = 0;
		
		__class_hierarchy_descriptor_t const * 
				__class_desc = __col.class_descriptor();

		__base_class_array_t const * __base_classes = __class_desc->base_classes();
			
		for( uint32_t __ix = 0; __ix < __class_desc->size(); __ix++ )
		{
			__base_class_descriptor_t const * 
				__base_class =  __base_classes->array[ __ix ];

			type_info const & 
				__base_type = * __base_class->type_descriptor;
			
			//for each polimorphic type there is only one static instancje of type_info
			if( __comparator( __base_type, __cast_type ) )
			{
				//pointer of real object
				void * __src_this_ptr = static_cast<char *>( __cpp_obj ) - __col.offset();
				__this_ptr = __base_class->compute_this_ptr( __src_this_ptr );
				break;
			}
		}

		return __this_ptr;
	}

	template<typename __exception_type>
	static inline __exception_type __format_exception( void * __cpp_obj, char * __fmt, __exception_type * __unused = 0 ) 
	{
		char buff[120];
		sprintf( buff ,__fmt ,__cpp_obj );
		return __exception_type( buff );
	}
}

extern "C"
void * __cdecl __RTDynamicCast ( void * __cpp_obj
								, long __VfDelta
								, void * __p_in_type
								, void * __p_cast_type
								, int __throw_on_error ) // throw (...)
{
	using namespace rtti;

	void * __dest_ptr = 0;

	if( __cpp_obj != 0 )
	{
		try 
		{

			type_info const & __in_type = *static_cast<type_info const *>(__p_in_type);
			type_info const & __cast_type = *static_cast<type_info const *>(__p_cast_type);

			__complete_object_locator_t const * 
				__col = __complete_object_locator_t::from_cpp_object( __cpp_obj );
		
			//at first pass comparing addresses of mangled names
			//at second pass by by mangled name
			//FIXME:
			//  - for each type there is only one instance of type info with one static mangled name
			//	- if even for some reason in some circumstances there are multiple instances
			//	  second pass will find them in slower way, but will find them finaly
			//	- first pass by comapring mangled name pointers is fast
			//	- in most (or even all cases) if first pass fails scond will fail too
			//	- if both fails we dont need to hurry becouse at end we will throw bad_cast ....

			__dest_ptr = __rt_dynamic_cast_iterator(__cpp_obj, __cast_type, *__col, __type_info_ptr_copare() );
			if( __dest_ptr == 0 )
				__dest_ptr = __rt_dynamic_cast_iterator(__cpp_obj, __cast_type, *__col, __type_info_name_copare() );
		}
		catch(...)
		{
			
			if( __throw_on_error > 0 ) 
				throw __format_exception<std::__non_rtti_object>( 
				__cpp_obj, "__RTDynamicCast non RTTI capable object at address %x");
	
			__dest_ptr = 0;
		}

		if( __dest_ptr == 0 && __throw_on_error > 0 )
			throw __format_exception<std::bad_cast>( __cpp_obj, "bad cast of object at address %x");
	}

	return __dest_ptr;
}

extern "C"
void * __cdecl __RTCastToVoid( void * __cpp_obj )
{
	using namespace rtti;

	void * __dest_ptr = 0;

	if( __cpp_obj != 0 )
	{
		try 
		{
			__complete_object_locator_t const * 
				__col = __complete_object_locator_t::from_cpp_object( __cpp_obj );

			__dest_ptr = static_cast<char *>(__cpp_obj) - __col->offset();
		}
		catch(...)
		{
			throw __format_exception<std::__non_rtti_object>( 
				__cpp_obj, "__RTCastToVoid non RTTI capable object at address %x");
		}
	}

	return __dest_ptr;
}
extern "C"
void *__cdecl __RTtypeid( void * __cpp_obj )
{
	using namespace rtti;

	if( __cpp_obj != 0 )
	{
		try 
		{
			__complete_object_locator_t const * 
				__col = __complete_object_locator_t::from_cpp_object( __cpp_obj );
			type_info const * __info = __col->type_descriptor();
			return const_cast<type_info *>(__info);
		}
		catch(...)
		{
			throw __format_exception<std::__non_rtti_object>( 
				__cpp_obj, "__RTtypeid non RTTI capable object at address %x");
		}
	}
	throw std::bad_typeid("null pointer passed to typeid");
}